#pragma once

#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>
#include "Cond.hpp"
#include "Mutex.hpp"

using namespace MutexModule;
using namespace CondModule;

const int defaultcap = 5;

template <typename T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _q.size() >= _cap;
    }

    bool IsEmpty()
    {
        return _q.size() <= 0;
    }

public:
    BlockQueue(int cap = defaultcap)
        : _cap(cap),
          _csleep_num(0),
          _psleep_num(0)
    {}

    // 生产者调用向队列中入数据
    void Push(const T &in)
    {
        LockGuard guard(_mutex);
        while (IsFull())
        {
            _psleep_num++;
            std::cout << "生产者线程进行休眠: _psleep_num: " << _psleep_num << std::endl;
            _full_cond.Wait(_mutex);
            _psleep_num--;
        }
        _q.push(in);
        if (_csleep_num > 0)    // 条件变量中没有等待的线程时进行唤醒也不影响，这里增加判断增强代码的逻辑性
        {
            _empty_cond.Signal();
            std::cout << "唤醒一个消费者... " << std::endl;
        }
    }

    // 消费者调用向队列中取数据
    T Pop()
    {
        LockGuard guard(_mutex);
        while (IsEmpty())
        {
            _csleep_num++;
            std::cout << "消费者线程进行休眠: _csleep_num: " << _csleep_num << std::endl;
            _empty_cond.Wait(_mutex);
            _csleep_num--;
        }
        T data = _q.front();
        _q.pop();
        if (_psleep_num > 0)
        {
            std::cout << "唤醒一个生产者..." << std::endl;
            _full_cond.Signal();
        }
        return data;
    }

    ~BlockQueue()
    {}

private:
    std::queue<T> _q; // 缓冲区 -- 临界资源
    int _cap;         //  缓冲区大小

    // 创建阻塞队列的时候创建以下成员变量，创建时会自动调用自己的构造函数进行初始化
    // 析构时也会自动调用自己的析构函数进行销毁
    Mutex _mutex;
    Cond _full_cond;  // 生产者等待的条件变量
    Cond _empty_cond; // 消费者等待的条件变量

    int _csleep_num; // 消费者休眠个数
    int _psleep_num; // 生产者休眠个数
};